package com.game.logic.fighting.pve;

import akka.actor.ActorRef;
import com.coment.cfg.luban.*;
import com.coment.cfg.luban.constant.AIDCFG;
import com.coment.cfg.luban.constant.ItemsType;
import com.coment.cfg.luban.items.EquipCfg;
import com.coment.game.core.GameActionMessage;
import com.coment.game.core.GameActionMessageType;
import com.coment.game.core.RewardResults;
import com.coment.message.BloodVolumeSynchronizationMessage;
import com.coment.message.DrawResult;
import com.coment.message.MonsterPushMessage;
import com.coment.message.RolesActionType;
import com.coment.message.game.PVERewards;
import com.coment.message.game.RankingRewards;
import com.coment.message.roles.*;
import com.coment.po.AnchorInfo;
import com.coment.po.Game;
import com.coment.po.UserPlayInfo;
import com.coment.po.roles.FightPanel;
import com.coment.po.roles.Roles;
import com.game.dao.core.GameDao;
import com.game.dao.living.AnchorInfoDao;
import com.game.dao.user.UserPlayInfoDao;
import com.game.dao.user.roles.RolesDao;
import com.game.framework.ActorManager;
import com.game.framework.lock.LooksManager;
import com.game.logic.core.comment.Bonus;
import com.game.logic.core.comment.BonusPoll;
import com.game.logic.core.ranking.RakingService;
import com.game.logic.cultivate.FightingCapacityService;
import com.game.logic.drawCards.DrawCardsServiceImpl;
import com.game.logic.fighting.actor.RolesActor;
import com.game.logic.items.ItemsService;
import com.game.logic.items.bonus.BonusService;
import com.game.logic.ranking.GameRakingService;
import com.game.logic.task.UserTaskInfoService;
import com.game.logic.user.UserGameRolesDao;
import com.sdk.service.impl.SdkService;
import com.xs.action.exection.ActionException;
import com.xs.build.server.websocket.channel.ClientManager;
import jakarta.annotation.Resource;
import lombok.Synchronized;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.validation.constraints.NotNull;
import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
import java.util.stream.Stream;


@Slf4j
@Service
public class GameServiceImpl {
    @Resource
    private GameDao gameDao;
    @Resource
    private SdkService sdkService;
    @Resource
    private RolesDao rolesDao;
    @Resource
    private GameRakingService rakingService;
    @Resource
    private AnchorInfoDao anchorInfoDao;
    @Resource
    private UserPlayInfoDao userPlayInfoDao;
    @Resource
    private UserTaskInfoService userTaskInfoService;
    @Resource
    private FightingCapacityService fightingCapacityService;
    @Resource
    private UserGameRolesDao userGameRolesDao;
    @Resource
    private ItemsService itemsService;

    /// 踢人+拉黑
    public void kickAndBlacklistPeople(@NotNull Integer roomId, Integer userInfoId) {
        Game select = this.gameDao.findById(roomId).orElseThrow(() -> new ActionException("游戏不存在"));
        if (select.userInfoIdSeat.containsKey(userInfoId)) {
            select.blackList.add(userInfoId);
            select.userInfoIdSeat.remove(userInfoId);
        }
        this.gameDao.save(select);
    }

    /// 加载关卡配置
    public Game initializeGame(
            Integer anchorInfoId,
            Integer level
    ) {
        Game select = this.gameDao.findById(anchorInfoId).orElseGet(() ->
                gameDao.save(new Game(anchorInfoId))
        );

        if (select.getStartTime() != null) {
            throw new ActionException("游戏已开始");
        }

        if (select.monsterSet.isEmpty()) {
            select.setLevel(level);
            this.gameDao.save(select);
            this.loadLevelInfo(select);
        }

        if (select.getStartTime() == null && !select.userInfoIdSeat.isEmpty()) this.tryStartGame(select);

        return select;
    }


    /// 加载关卡配置
    @Async
    public void loadLevelInfo(Game game) {
        Integer anchorInfoId = game.getAnchorInfoId();
        int level = game.getLevel();
        LevelConfig levelConfig = Tables.cfg.get_tblevelconfig().get(level);

        for (String monsterId : game.monsterSet) {
            rolesDao.deleteById(monsterId);
        }
        game.monsterSet.clear();
        Map<String, Integer> rolesIdAndCardId = new HashMap<>();

        new HashMap<Integer, Integer>() {
            {
                put(levelConfig.MonsterID, levelConfig.MonsterNum);
                put(levelConfig.EliteMonsterID, levelConfig.EliteMonsterNum);
                put(levelConfig.BossID, levelConfig.BossNum);
            }
        }.forEach((cardId, value) -> {
            MonsterConfig cfg = Tables.cfg.get_tbmonsterconfig().get(cardId);
            if (cfg != null) {
                for (int i = 0; i < value; i++) {
                    Roles roles = new Roles(cfg.ID);
                    Roles save = rolesDao.save(roles);
                    rolesIdAndCardId.put(save.getId(), cardId);
                    game.monsterSet.add(save.getId());
                }
            } else {
                log.debug("角色不存在{}", cardId);
            }
        });
        gameDao.save(game);
        ClientManager.sendMessageToClient(anchorInfoId.toString(), new MonsterPushMessage(rolesIdAndCardId));
    }

    /// 尝试开始游戏
    private void tryStartGame(Game select) {
        Integer anchorInfoId = select.getAnchorInfoId();
        Long startTime = select.getStartTime();
        if (startTime != null) {
            log.error("游戏已开始:{}", anchorInfoId);
            throw new ActionException("游戏已开始");
//            return;
        }
        if (select.userInfoIdSeat.isEmpty()) {
            log.debug("人数不足无法开始");
            throw new ActionException("人数不足无法开始");
//            return;
        }
        if (select.monsterSet.isEmpty()) {
            log.error("怪物未加载");
            return;
        }
        select.userInfoIdSeat.forEach((userInfoId, rolesId) ->
                rakingService.initialize(anchorInfoId, userInfoId, rolesId)
        );

        select.setStartTime(System.currentTimeMillis());
        this.gameDao.save(select);
        GameTime.activeRooms.add(anchorInfoId);

        synchronizeAPIMessages(select, anchorInfoId);
    }

    /// 同步游戏状态
    @Async
    public void synchronizeAPIMessages(Game select, Integer anchorInfoId) {
        AnchorInfo anchorInfo = this.anchorInfoDao.findById(anchorInfoId).orElseThrow();
        long startGame = select.getStartTime() == null ? 0L : select.getStartTime();
        String apiType = anchorInfo.getApiType();
        int roundId = select.level;
        String roomId = anchorInfo.getRoomId();
        String anchorOpenId = anchorInfo.getAnchorOpenId();
        long time = System.currentTimeMillis();
        int status = startGame == 0L ? 1 : 2;
        SdkService.SyncRoundStatus syncRoundStatus = new SdkService.SyncRoundStatus(roundId, roomId, anchorOpenId, time, "", status);
        this.sdkService.synchronizeAPIMessages(apiType, syncRoundStatus);
    }

    /// 打扫房间
    @Synchronized
    public void clear(@NotNull Integer anchorInfoId) {
        Game po = this.gameDao.findByIdMust(anchorInfoId);

        for (String monsterId : po.monsterSet) {
            rolesDao.removeById(monsterId);
        }
        for (String rolesId : po.userInfoIdSeat.values()) {
            ActorManager.getActor(RolesActor.class, rolesId).tell(new ExitPVE(), ActorRef.noSender());
        }

        Game game = new Game(anchorInfoId);
        game.userInfoIdSeat = po.userInfoIdSeat;
        for (String rolesId : game.userInfoIdSeat.values()) {
            Roles roles;
            try {
                roles = rolesDao.findByIdMust(rolesId);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
            roles.rolePanel.statusPanel.status.clear();
            roles.rolePanel.fightPanel = new FightPanel();
            rolesDao.save(roles);
        }

        game.blackList = po.getBlackList();
        game.level = -1;
        game.setStartTime(null);
        this.rakingService.removeAll(anchorInfoId);
        this.gameDao.save(game);
        GameTime.activeRooms.remove(anchorInfoId);
    }

    public boolean tryEndBattle(Game select) {
        Integer anchorInfoId = select.getAnchorInfoId();
        int time = Tables.cfg.get_tblevelconfig().get(select.level).Time;
        Long startTime = select.getStartTime();
        long endTime = startTime + (long) time * 1000L;
        long currentTimeMillis = System.currentTimeMillis();
        long remaining = endTime - currentTimeMillis;

        GameActionMessage actionMessage = new GameActionMessage();
        actionMessage.anchorInfoId = anchorInfoId;

        boolean end = false;
        if (select.getStartTime() == null) { // 未开始
            actionMessage.type = GameActionMessageType.END_PVE.value;
            end = true;
        } else if (remaining < 0L) { // 超时
            actionMessage.type = GameActionMessageType.LOSE_PVE.value;
            ClientManager.sendMessageToClient(anchorInfoId, actionMessage);
            clear(anchorInfoId);
            end = true;
        }
        return end;
    }


    /// 入座
    public RolesActionMessage beSeated(
            Integer userInfoId,
            int newAnchorInfoId,
            String gameNewRolesId
    ) {
        Game game = this.gameDao.findByIdMust(newAnchorInfoId);
        Roles seatRoles = this.rolesDao.findByIdMust(gameNewRolesId);

        Optional<String> gameOldRolesId = Optional.ofNullable(game.userInfoIdSeat.get(userInfoId));

        ///  1.1 如果已经入座
        boolean oldRolesPresent = gameOldRolesId.isPresent();
        boolean newRolesIdSeated = oldRolesPresent && gameOldRolesId.get().equals(gameNewRolesId);
        if (newRolesIdSeated) {
            RolesActionMessage rolesActionMessage = new RolesActionMessage(
                    userInfoId,
                    seatRoles.getId(),
                    seatRoles.getCardId(),
                    RolesActionType.SEAT.getValue()
            );
            rolesActionMessage.attrs = seatRoles.rolePanel.getAttribute();
            rolesActionMessage.attrs.put(AIDCFG.fightingCapacity, fightingCapacityService.getRolesFightingCapacity(gameNewRolesId));


            return rolesActionMessage;
        }

        /// 1.2 拉黑
        if (game.blackList.contains(userInfoId)) {
            throw new ActionException("用户被拉黑");
        }


        UserPlayInfo userPlayInfo = userPlayInfoDao.findByUserInfoIdOrCreate(userInfoId);

        /// 2.1 换直播间?
        boolean houseExchange = userPlayInfo.recentlyPlayedAnchorInfoId != newAnchorInfoId;
        if (houseExchange) {
            // 清空旧的直播间
            this.rakingService.removeAll(userPlayInfo.recentlyPlayedAnchorInfoId, userInfoId, gameNewRolesId);

            // 移除 xx用户 座位
            int removeRomId = userPlayInfo.recentlyPlayedAnchorInfoId;

            if (removeRomId != 0) {
                leaveTheSeatInTheLiveBroadcastRoom(removeRomId, userInfoId);
            }
        }

        /// 2.2 是否换角色?
        boolean roleReversal = oldRolesPresent && !gameOldRolesId.get().equals(gameNewRolesId);
        if (roleReversal) {
            this.rakingService.removeAll(newAnchorInfoId, userInfoId, gameOldRolesId.get());
            game.userInfoIdSeat.remove(userInfoId);

            Action<Object> action = new Action<>(RolesCmd.STOP_Cultivate.getCmd(), null);
            ActorManager.getActor(RolesActor.class, gameNewRolesId).tell(action, ActorRef.noSender());
        }

        /// 4.0 加入座位
        this.rakingService.initialize(newAnchorInfoId, userInfoId, gameNewRolesId);
        game.userInfoIdSeat.put(userInfoId, gameNewRolesId);
        Game save = gameDao.save(game);

        /// 4.1 玩家留痕 上次入座房间|使用角色
        userPlayInfoDao.updateRecentlyPlayedAnchorInfoId(userInfoId, newAnchorInfoId);
        userPlayInfoDao.updateRecentlyPlayedRolesId(userInfoId, gameNewRolesId);


        /// 5.1 尝试开始游戏
        boolean theGameHasNotStartedYet = game.getStartTime() == null;
        if (theGameHasNotStartedYet) this.tryStartGame(game);


        /// 6.1 操作角色
        boolean gameStart = save.getStartTime() != null;
        if (gameStart) {
            // 开始修炼
            Action<Object> action = new Action<>(RolesCmd.START_Cultivate.getCmd(), null);
            ActorManager.getActor(RolesActor.class, gameNewRolesId).tell(action, ActorRef.noSender());
        }

        RolesActionMessage rolesActionMessage = new RolesActionMessage(
                userInfoId,
                seatRoles.getId(),
                seatRoles.getCardId(),
                RolesActionType.SEAT.getValue()
        );
        rolesActionMessage.attrs = seatRoles.rolePanel.getAttribute();
        rolesActionMessage.attrs.put(AIDCFG.fightingCapacity, fightingCapacityService.getRolesFightingCapacity(gameNewRolesId));
        return rolesActionMessage;
    }

    /// 离开指定 直播间 座位{线程安全,SH}
    private void leaveTheSeatInTheLiveBroadcastRoom(int removeRomId, Integer removeUserInfoId) {
        ReentrantLock writeLock = LooksManager.getWriteLock(Game.class, removeRomId);
        writeLock.lock();
        try {
            Game old = gameDao.findByIdMust(removeRomId);
            String removeRolesId = old.userInfoIdSeat.remove(removeUserInfoId);
            Optional<String> removeRolesIdOpt = Optional.ofNullable(removeRolesId);
            if (removeRolesIdOpt.isPresent()) {
                Action<Object> action = new Action<>(RolesCmd.STOP_Cultivate.getCmd(), null);
                ActorManager.getActor(RolesActor.class, removeRolesId).tell(action, ActorRef.noSender());

                UserPlayInfo userPlayInfo = userPlayInfoDao.findByUserInfoIdOrCreate(removeUserInfoId);
                ClientManager.sendMessageToClient(userPlayInfo.recentlyPlayedAnchorInfoId, old);
                gameDao.save(old);
            }
        } finally {
            writeLock.unlock();
        }
    }

    /// 处理掉落奖励
    private List<RewardResults> processDropRewards(HashMap<Integer, Integer> dropValues) {
        if (dropValues == null || dropValues.isEmpty()) {
            return Collections.emptyList();
        }

        // 根据掉落ID获取奖励配置，按组分类
        Map<Integer, List<Bonus<Integer>>> bonusByGroup = dropValues.keySet().stream()
                .map(BonusService.bonusPoll::get)
                .collect(Collectors.groupingBy(Bonus::getGroup, Collectors.toList()));

        return getRewardResults(bonusByGroup);
    }

    // 处理所有组的奖励
    private static List<RewardResults> getRewardResults(Map<Integer, List<Bonus<Integer>>> bonusByGroup) {
        return bonusByGroup.entrySet().stream()
                .flatMap(groupEntry -> {
                    List<Bonus<Integer>> groupBonuses = groupEntry.getValue();
                    if (groupBonuses == null || groupBonuses.isEmpty()) {
                        return Stream.empty();
                    }

                    // 展开奖励物品并按ID分组统计
                    Map<Integer, List<Integer>> itemCountMap = groupBonuses.stream()
                            .map(Bonus::getBonusValue)
                            .flatMap(List::stream)
                            .collect(Collectors.groupingBy(id -> id));

                    Integer groupId = groupEntry.getKey();
                    return switch (groupId) {
                        case -1 -> Stream.empty();
                        case ItemsType.Equip -> itemCountMap.entrySet().stream().map(entry -> {
                            Integer key = entry.getKey();
                            EquipCfg cfg = Tables.cfg.get_tbequipcfg().get(key);
                            if (cfg == null) {
                                log.error("EquipCfg not found: {}-", key);
                                return null;
                            }
                            return new RewardResults(
                                    groupId,
                                    key,
                                    entry.getValue().size(),
                                    cfg.equipLevel
                            );
                        });
                        default -> itemCountMap.entrySet().stream().map(entry -> {
                            Integer key = entry.getKey();
                            return new RewardResults(
                                    groupId,
                                    key,
                                    entry.getValue().size(),
                                    1
                            );
                        });
                    };

                })
                .sorted(Comparator.comparingInt(RewardResults::getLevel).reversed())
                .collect(Collectors.toList());
    }


    public void attemptToWin(@NotNull Integer anchorInfoId) {
        Game game = this.gameDao.findById(anchorInfoId).orElseThrow();

        if (game.level == -1) {
            GameActionMessage actionMessage = new GameActionMessage();
            actionMessage.type = GameActionMessageType.END_PVE.value;
            ClientManager.sendMessageToClient(anchorInfoId, actionMessage);
            return;
        }


        BloodVolumeSynchronizationMessage bloodVolumeSynchronizationMessage = userGameRolesDao.survivalCharacter(anchorInfoId);
        boolean noWIn = !bloodVolumeSynchronizationMessage.rolesHp().isEmpty();
        if (noWIn) {
            ClientManager.sendMessageToClient(anchorInfoId, bloodVolumeSynchronizationMessage);
            log.error("游戏未结束{}", bloodVolumeSynchronizationMessage);
        }

        boolean win = userGameRolesDao.gameIsWin(anchorInfoId);
//        boolean win = true;

        if (win) {


            AnchorInfo anchorInfo = this.anchorInfoDao.findById(anchorInfoId).orElseThrow();
            String apiType = anchorInfo.getApiType();
            SdkService.SyncRoundStatus syncRoundStatus = getSyncRoundStatus(game, anchorInfo);
            this.sdkService.synchronizeAPIMessages(apiType, syncRoundStatus);

            int level = game.level;

            LevelExperienceCfg levelExperienceCfg = Tables.cfg.get_tblevelexperiencecfg().get(level);
            LevelDropCfg levelDropCfg = Tables.cfg.get_tbleveldropcfg().get(level);

            try {
                // 2.0 版本
                // 2.1 排行贡献
                List<RakingService.RankInfo<Object>> allContributionRRankInfo = rakingService.getAllContributionRRankInfo(anchorInfoId);
                userTaskInfoService.tryCompleteTask(
                        game.level,
                        allContributionRRankInfo
                );


                List<RankingRewards> list = new ArrayList<>();
                for (RakingService.RankInfo<Object> rankingInfo : allContributionRRankInfo) {
                    Integer userId = (Integer) rankingInfo.id();
                    RankingRewards rankingRewards = new RankingRewards();
                    rankingRewards.setRolesId(game.userInfoIdSeat.get(userId));
                    rankingRewards.setUserId(userId);
                    rankingRewards.setNo(rankingInfo.rank());
                    rankingRewards.setPoints(rankingInfo.score());
                    rankingRewards.setRewardResults(new ArrayList<>());
                    list.add(rankingRewards);
                }
                log.debug("2.0 排行贡献: {}", list);
                // 2.2 掉落经验
                double LastExp = levelExperienceCfg.value.getLast() * levelExperienceCfg.baseValue;
                int LastIndex = levelExperienceCfg.value.size() - 1;
                for (int i = 0; i < list.size(); i++) {
                    if (i >= LastIndex) {
                        list.get(i).setExp(LastExp);
                    } else {
                        list.get(i).setExp(levelExperienceCfg.value.get(i) * levelExperienceCfg.baseValue);
                    }
                }
                log.debug("2.0 掉落经验: {}", list);
                // 2.3 掉落物品
                Optional<BonusPoll<Integer>> levelDrop = Optional.ofNullable(DrawCardsServiceImpl.levelDropPoll.get(level));
                levelDrop.ifPresent(bonusPoll -> {

                    Map<Integer, List<Bonus<Integer>>> mapBonus = levelDrop.orElseThrow()
                            .obtainDropBonus()
                            .stream()
                            .collect(Collectors.groupingBy(Bonus::getGroup));


                    // 2.3.1 排名分配
                    List<RewardResults> rewardResults = getRewardResults(mapBonus);
                    int LastRewardResultsIndex = rewardResults.size() - 1;
                    for (int i = 0; i < list.size(); i++) {
                        if (i < LastRewardResultsIndex) {
                            list.get(i).getRewardResults().add(rewardResults.get(i));
                        }
                    }

                    // 2.3.2 排名固定掉落
                    if (Optional.ofNullable(levelDropCfg).isPresent()) {
                        List<HashMap<Integer, Integer>> fixRakingDrop;

                        fixRakingDrop = List.of(
                                levelDropCfg.value1,
                                levelDropCfg.value2,
                                levelDropCfg.value3,
                                levelDropCfg.value4,
                                levelDropCfg.value5
                        );


                        int maxIndex = Math.min(list.size(), fixRakingDrop.size());
                        for (int i = 0; i < maxIndex; i++) {
                            if (i < LastRewardResultsIndex) {
                                List<RewardResults> fixDropByNo = this.processDropRewards(fixRakingDrop.get(i));

                                list.get(i).getRewardResults().addAll(fixDropByNo);
                            }
                        }
                    }
                });
                PVERewards pveRewards = new PVERewards(list);
                ClientManager.sendMessageToClient(anchorInfoId, pveRewards);

                tryObtainLevelRewards(pveRewards);
            } catch (Exception e) {
                log.error("2.0 排名信息错误:", e);
            }

            clear(anchorInfoId);
        }
    }

    @Async
    public void tryObtainLevelRewards(PVERewards pveRewards) {
        for (RankingRewards reward : pveRewards.rewards()) {
            String rolesId = reward.getRolesId();
            Integer userId = reward.getUserId();

            ActorRef rolesActor = null;
            try {
                rolesActor = ActorManager.getActor(RolesActor.class, rolesId);
            } catch (Exception e) {
                log.debug("获取actor失败", e);
                // 下一循环
                continue;
            }

            rolesActor.tell(new ObtainExp(reward.getExp()), ActorRef.noSender());

            for (RewardResults result : reward.getRewardResults()) {
                int g = result.g;
                int id = result.id;
                int num = result.num;

                switch (g) {
                    case ItemsType.Bonus:
                        log.info("2.0 掉落Bonus: {}", result);
                        List<DrawResult> drawResults = itemsService.obtainItems(userId, g, id);
                        log.info("2.0 掉落Bonus->drawResults: {}", drawResults);
                        break;
                    case ItemsType.Equip:
                        Equip equip = new Equip(rolesId, id, num);
                        rolesActor.tell(equip, ActorRef.noSender());
                        break;
                }
            }
        }
    }

    private static SdkService.SyncRoundStatus getSyncRoundStatus(Game select, AnchorInfo anchorInfo) {
        int roundId = select.getLevel();
        String roomId = anchorInfo.getRoomId();
        String anchorOpenId = anchorInfo.getAnchorOpenId();

        int status;
        if (select.getStartTime() != null) {
            status = select.getStartTime() == 0L ? 1 : 2;
        } else {
            status = 1;
        }
        return new SdkService.SyncRoundStatus(
                roundId, roomId, anchorOpenId, 0L, "", status
        );
    }
}
